#include <kuai/sim/pvt.hpp>

namespace kuai {

	/*
	String PropertyTemperature0::get(const String& key) const {
		if (key == "kinetic_energy") {
			return str(kinetic_energy());
		}
		else {
			return SingleProperty<RealNumber>::get(key);
		}
	};

	PropertyTemperature0::PropertyTemperature0(PhaseSpace& space)
		: SingleProperty<RealNumber>(0)
	{
		nConstrains = 0;
		nAtoms = space.countAtoms();
	}

	PropertyTemperature::PropertyTemperature(PhaseSpace& space) 
		: PropertyTemperature0(space)
	{
		speeds = static_cast<PropertyXYZ&>(*space[SIM_INFO_ATOM_SPEEDS]).x_ptr();
		weights = static_cast<PropertyRealNumber&>(*space[MOL_INFO_ATOM_MASS]).x_ptr();
	}

	void PropertyTemperature::calcTemperature(SimulationEnvironment& env) {
		size_t i1 = env.blockStart(nAtoms);
		size_t i2 = env.blockFinish(nAtoms);
		RealNumber resultI = 0;
		for (size_t i = i1; i < i2; ++i) {
			resultI += weights[i]*dot(speeds[i], speeds[i]);
		}
		// resultI *= 10000/2; // Convert to kJ/mol
		// resultI /= (3 * nAtoms - nConstrains) / 1000.0 / 2.0 * SI::BOLTZMANN_CONSTANT * SI::AVOGADRO_CONSTANT;
		resultI *=  RealNumber(1e7) / ((3 * nAtoms - nConstrains)  * SI::BOLTZMANN_CONSTANT * SI::AVOGADRO_CONSTANT);

		if (env.tid == 0) {
			x_ref() = 0;
		}
		env.barrier->wait();
		env.mutex->lock();
		x_ref() += resultI;
		env.mutex->unlock();
		env.barrier->wait();
	}
		
	void PropertyTemperature::scaleSpeed(SimulationEnvironment& env, RealNumber factor) {
		size_t i1 = env.blockStart(nAtoms);
		size_t i2 = env.blockFinish(nAtoms);
		
		for (size_t i = i1; i < i2; ++i) {
			speeds[i] *= factor;
		}
		if (env.tid == 0) {
			x_ref() *= factor * factor;
		}
		env.barrier->wait();
	};
		
	void PropertyTemperature::resetTemperature(SimulationEnvironment& env, RealNumber target) {
		if (env.tid == 0) {
			XYZ p(0, 0, 0);
			for (size_t i = 0; i < nAtoms; ++i) {
				speeds[i].x = env.die_norm();
				speeds[i].y = env.die_norm();
				speeds[i].z = env.die_norm();
				p.x += weights[i] * speeds[i].x;
				p.y += weights[i] * speeds[i].y;
				p.z += weights[i] * speeds[i].z;
			}
			p /= nAtoms;
			for (size_t i = 0; i < nAtoms; ++i) {
				speeds[i].x -= p.x / weights[i];
				speeds[i].y -= p.y / weights[i];
				speeds[i].z -= p.z / weights[i];
			}
		}
		env.barrier->wait();
		calcTemperature(env);
		RealNumber factor = sqrt(target / c_ref());
		scaleSpeed(env, factor);
	}

		
	void PropertyTemperature::start(SimulationEnvironment& env, PhaseSpace& space, EnergySet& efunc) {
		if (init) {
			resetTemperature(env, *init);
		}
		else {
			calcTemperature(env);
		}
	}
	
	void PropertyTemperature::act(SimulationEnvironment& env, size_t iStep, PhaseSpace& space, EnergySet& efunc) {
		calcTemperature(env);
	}

	void PropertyTemperature::set(const String& key, const String& data) {
		if (key == "initial") {
			try {
				init = lexical_cast<RealNumber>(data);
			}
			catch (BadLexicalCast&) {
				try {
					bool reset = lexical_cast<bool>(data);
					if (!reset) {
						init.reset();
						return;
					}
				}
				catch (BadLexicalCast&) {
				}
				throw BadLexicalCast(data, typeid(RealNumber));
			}
		}
		else if (key == "interval") {
			SpaceControl::set(key, data);
		}
		else {
			PropertyTemperature0::set(key, data);
		}
	};
	String PropertyTemperature::get(const String& key) const {
		if (key == "initial") {
			if (init) {
				return str(*init);
			}
			else {
				return "no";
			}
		}
		else if (key == "interval") {
			return SpaceControl::get(key);
		}
		else {
			return PropertyTemperature0::get(key);
		}
	};
	
	
	void TemperatureControl::start(SimulationEnvironment& env, PhaseSpace& space, EnergySet& efunc) {
		if (env.tid == 0) {
			target = target0;
		}
		PropertyTemperature::start(env, space, efunc);
	};
		
	void TemperatureControl::set(const String& key, const String& data) {
		if (key == "target") {
			target0 = lexical_cast<RealNumber>(data);
			if (target0 < 0) {
				throw error("Temperature target must > 0");
			}
		}
		else if (key == "delta") {
			delta = lexical_cast<RealNumber>(data);
		}
		else {
			PropertyTemperature::set(key, data);
		}
	};
		
	
	String TemperatureControl::get(const String& key) const {
		if (key == "target0") {
			return str(target0);
		}
		else if (key == "target") {
			return str(target);
		}
		else if (key == "delta") {
			return str(delta);
		}
		else {
			return PropertyTemperature::get(key);
		}
	};


	BrendsonTemperatureControl::BrendsonTemperatureControl(PhaseSpace& space) 
		: TemperatureControl(space)
	{
		compressibility = RealNumber(1) / 100;
	};

	void BrendsonTemperatureControl::act(SimulationEnvironment& env, size_t iStep, PhaseSpace& space, EnergySet& efunc) {
		calcTemperature(env);
		if (env.tid == 0) {
			factor = sqrt(1 + compressibility * (target/c_ref() - 1));
			target += delta;
		}
		env.barrier->wait();
		scaleSpeed(env, factor);
	}

	void BrendsonTemperatureControl::set(const String& key, const String& data) {
		if (key == "tau") {
			compressibility = lexical_cast<RealNumber>(data);
			if (compressibility < 1) {
				throw error("tau must >= 1");
			}
			else {
				compressibility = 1 / compressibility;
			}
		}
		else {
			TemperatureControl::set(key, data);	
		}
	};
		
	String BrendsonTemperatureControl::get(const String& key) const {
		if (key == "tau") {
			return str(1/compressibility);
		}
		else {
			return TemperatureControl::get(key);	
		}
	};

	
	void AndersonTemperatureControl::act(SimulationEnvironment& env, size_t iStep, PhaseSpace& space, EnergySet& efunc) {
		if (env.tid == 0) {
			die = env.die_01();
			target +=  delta;
		}
		env.barrier->wait();
		if (die < possibility) {
			resetTemperature(env, target-delta);
		}
	}

	void AndersonTemperatureControl::set(const String& key, const String& data) {
		if (key == "possibility") {
			possibility = lexical_cast<RealNumber>(data);
			if (possibility <= 0 || possibility > 1) {
				throw error("possibility should between (0, 1]");
			}
		}
		else {
			TemperatureControl::set(key, data);
		}
	}
		
	String AndersonTemperatureControl::get(const String& key) const {
		if (key == "possibility") {
			return str(possibility);
		}
		else if (key == "die") {
			return str(die);
		}
		else {
			return TemperatureControl::get(key);
		}
	}
	*/
}
